package com.smasher.stepview

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.text.TextUtils
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.IntRange
import androidx.core.content.res.use
import kotlin.math.max
import kotlin.math.min


/**
 * TimeLineView
 *
 * @author WrBug
 * @since 2017/2/21
 */
class StepLineView : View {

    private val mPaint = Paint()

    /**
     * 状态文本
     */
    private var mPointTxt: MutableList<String> = ArrayList()

    /**
     * 步数
     */
    private var mStep = 1

    /**
     * 圆形x坐标组
     */
    private var mXpoints = IntArray(3)

    private var mPreLineColor = Color.GRAY
    private var mStartedLineColor = Color.BLUE

    private var mStartedCircleColor = Color.BLUE
    private var mUnderwayCircleColor = Color.GREEN
    private var mPreCircleColor = Color.GRAY

    private var mStartedStringColor = Color.BLUE
    private var mUnderwayStringColor = Color.GREEN
    private var mPreStringColor = Color.GRAY

    private var mRadius = 0f
    private var mTextSize = 0f
    private var mLineWidth = 0f
    private var mGap = 0f
    private var mTextHalf = 0f

    private var mOnStepChangedListener: OnStepChangedListener? = null


    constructor(context: Context) : this(context, null)

    constructor(
        context: Context,
        attributeSet: AttributeSet? = null
    ) : this(context, attributeSet, 0)

    constructor(
        context: Context,
        attributeSet: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : super(context, attributeSet, defStyleAttr) {
        if (attributeSet != null) {
            context.obtainStyledAttributes(attributeSet, R.styleable.StepLineView)
                .use { typedArray ->
                    mStartedLineColor =
                        typedArray.getColor(R.styleable.StepLineView_startedLineColor, Color.BLUE)
                    mPreLineColor =
                        typedArray.getColor(R.styleable.StepLineView_preLineColor, Color.GRAY)

                    mStartedCircleColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_startedCircleColor,
                            Color.BLUE
                        )
                    mUnderwayCircleColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_underwayCircleColor,
                            Color.GREEN
                        )
                    mPreCircleColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_preCircleColor,
                            Color.GRAY
                        )

                    mStartedStringColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_startedStringColor,
                            Color.BLUE
                        )
                    mUnderwayStringColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_underwayStringColor,
                            Color.GREEN
                        )
                    mPreStringColor =
                        typedArray.getColor(
                            R.styleable.StepLineView_preStringColor,
                            Color.GRAY
                        )

                    mTextSize =
                        typedArray.getDimension(R.styleable.StepLineView_textSize, 14f)
                    mRadius = typedArray.getDimension(R.styleable.StepLineView_radius, 24f)
                    mLineWidth =
                        typedArray.getDimension(R.styleable.StepLineView_lineWidth, 5f)
                    mGap =
                        typedArray.getDimension(R.styleable.StepLineView_gap, 25f)
                }
        }
        Log.i(TAG, "mTextSize: " + mTextSize)
        init()
    }

    private fun init() {
        this.mPaint.isAntiAlias = true
        this.mStep = 2
        mPointTxt.add("第一步")
        mPointTxt.add("第二步")
        mPointTxt.add("第三步")
        mPointTxt.add("第四步")
        mPointTxt.add("第五步")
        computeTextHalf()

    }


    /**
     * 未开始状态线条颜色
     *
     * @param preLineColor preLineColor
     * @return Builder
     */
    fun configPreLineColor(@ColorInt preLineColor: Int) {
        mPreLineColor = preLineColor
    }

    /**
     * 已进行状态线条颜色
     *
     * @param startedLineColor startedLineColor
     * @return Builder
     */
    fun configStartedLineColor(@ColorInt startedLineColor: Int) {
        mStartedLineColor = startedLineColor
    }


    /**
     * 未开始状态圆颜色
     *
     * @param preCircleColor preCircleColor
     * @return Builder
     */
    fun configPreCircleColor(@ColorInt preCircleColor: Int) {
        mPreCircleColor = preCircleColor
    }


    /**
     * 进行中状态圆颜色
     *
     * @param underwayCircleColor underwayCircleColor
     * @return Builder
     */
    fun configUnderwayCircleColor(@ColorInt underwayCircleColor: Int) {
        mUnderwayCircleColor = underwayCircleColor
    }

    /**
     * 已进行状态圆颜色
     *
     * @param startedCircleColor startedCircleColor
     * @return Builder
     */
    fun configStartedCircleColor(@ColorInt startedCircleColor: Int) {
        mStartedCircleColor = startedCircleColor
    }


    /**
     * 未开始状态文本颜色
     *
     * @param preStringColor preStringColor
     * @return Builder
     */
    fun configPreStringColor(@ColorInt preStringColor: Int) {
        mPreStringColor = preStringColor
    }


    /**
     * 进行中状态文本颜色
     *
     * @param underwayStringColor underwayStringColor
     * @return Builder
     */
    fun configUnderwayStringColor(@ColorInt underwayStringColor: Int) {
        mUnderwayStringColor = underwayStringColor
    }

    /**
     * 已进行状态文本颜色
     *
     * @param startedStringColor startedStringColor
     * @return Builder
     */
    fun configStartedStringColor(@ColorInt startedStringColor: Int) {
        mStartedStringColor = startedStringColor
    }


    /**
     * 文本大小
     *
     * @param size px
     */
    fun configTextSize(size: Float) {
        mTextSize = convertSpToPx(context, size)
        computeTextHalf()
    }

    /**
     * 线条宽度
     *
     * @param lineWidth lineWidth
     * @return Builder
     */
    fun configLineWidth(lineWidth: Float) {
        mLineWidth = lineWidth
    }

    /**
     * 刷新
     */
    fun applyConfigChange() {
        invalidate()
    }

//=================================================


    /**
     * 状态文本
     *
     * @param pointStringList pointStringList
     * @param step            step
     */
    fun setPointStrings(pointStringList: List<String>, @IntRange(from = 1) step: Int) {
        if (pointStringList.isEmpty()) {
            mPointTxt.clear()
            mStep = 0
        } else {
            mPointTxt = ArrayList(pointStringList)
            mStep = min(step.toDouble(), mPointTxt.size.toDouble()).toInt()
        }
        computeTextHalf()
        invalidate()
    }

    /**
     * 状态文本
     *
     * @param pointStringList pointStringList
     * @param step            step
     */
    fun setPointStrings(pointStringList: Array<String>, @IntRange(from = 1) step: Int) {
        if (pointStringList.isEmpty()) {
            mPointTxt.clear()
            mStep = 0
            computeTextHalf()
            invalidate()
        } else {
            setPointStrings(ArrayList(listOf(*pointStringList)), step)
        }
    }

    fun nextStep(): Boolean {
        if (mStep + 1 > mPointTxt.size) {
            return false
        } else {
            mStep++
            invalidate()
            return true
        }
    }

    fun previousStep(): Boolean {
        if (mStep - 1 < 1) {
            return false
        } else {
            mStep--
            invalidate()
            return true
        }
    }

    var step: Int
        get() = mStep
        set(step) {
            this.mStep =
                min(step.toDouble(), mPointTxt.size.toDouble()).toInt()
            invalidate()
        }


    fun setOnStepChangedListener(listener: OnStepChangedListener?) {
        this.mOnStepChangedListener = listener
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 解析 MeasureSpec
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        Log.i(
            TAG, "onMeasure:" + '\n' +
                    "widthMode = " + queryMeasureSpecMode(widthMode) + " withSize = " + widthSize + '\n' +
                    "heightMode = " + queryMeasureSpecMode(heightMode) + " heightSize = " + heightSize
        )

        val textHeight = computeTextHeight(mTextSize).toInt()
        // 计算期望的宽度和高度
        val desiredWidth = 0// 根据你的逻辑计算
        val desiredHeight =
            textHeight + 2 * this.mRadius + mGap + paddingTop + paddingBottom // 根据你的逻辑计算

        // 根据 Mode 来决定最终的宽度和高度
        val width = if (widthMode == MeasureSpec.EXACTLY) {
            widthSize
        } else {
            widthSize // Math.min(desiredWidth, widthSize);使用较小值确保不会超出父容器限制
        }

        val height = if (heightMode == MeasureSpec.EXACTLY) {
            heightSize
        } else {
            min(desiredHeight.toDouble(), heightSize.toDouble())
                .toInt() // 使用较小值确保不会超出父容器限制
        }

        Log.i(
            TAG, ("onMeasure:" + '\n' +
                    "with = " + width + " height = " + height)
        )
        // 设置测量后的尺寸
        setMeasuredDimension(width, height)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        Log.i(TAG, "onLayout: ")
    }

    override fun onDraw(canvas: Canvas) {
        if (initCalc()) {
            drawCircle(canvas, 1, mPointTxt[0], mXpoints[0])
            for (i in 1 until mPointTxt.size) {
                drawLine(
                    canvas, this.mStep > i,
                    mXpoints[(i - 1)], mXpoints[i]
                )
                drawCircle(
                    canvas, i + 1, mPointTxt[i],
                    mXpoints[i]
                )
            }
            if (mOnStepChangedListener != null) {
                mOnStepChangedListener!!.onChanged(
                    this, this.mStep,
                    mPointTxt[mStep - 1]
                )
            }
        }
    }

    private fun queryMeasureSpecMode(mode: Int): String {
        return when (mode) {
            MeasureSpec.UNSPECIFIED -> "UNSPECIFIED"
            MeasureSpec.AT_MOST -> "AT_MOST"
            MeasureSpec.EXACTLY -> "EXACTLY"
            else -> "UNKNOWN"
        }
    }

    private fun computeTextHeight(textSizeSp: Float): Float {
        // 创建并配置 Paint 对象

        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.textSize = textSizeSp

        // 获取 FontMetrics
        val fontMetrics = paint.fontMetrics

        // 计算文本高度
        return fontMetrics.bottom - fontMetrics.top
    }

    private fun computeTextWidth(text: String, textSizeSp: Float): Float {
        // 创建并配置 Paint 对象

        val paint = Paint()
        paint.textSize = textSizeSp

        // 测量文本宽度
        return paint.measureText(text)
    }

    private fun textSizeActual(): Float {
        return mTextSize
    }

    private fun initCalc(): Boolean {
        if (mPointTxt.isNotEmpty()) {
            val len = mPointTxt.size
            //第一条文本所占宽度的一半
            this.mXpoints = IntArray(len)
            mXpoints[0] = (paddingLeft + max(mTextHalf, mRadius)).toInt()
            //最后一条文本所占宽度
            mXpoints[len - 1] = (width - paddingEnd - max(mTextHalf, mRadius)).toInt()
            //步长平均宽度
            val dx = (mXpoints[len - 1] - mXpoints[0]) / (len - 1)
            for (i in 1 until mXpoints.size - 1) {
                mXpoints[i] = mXpoints[0] + dx * i
            }
            return true
        }
        return false
    }

    private fun computeTextHalf() {
        var computeTextWidth = 0f
        for (element in mPointTxt) {
            val width = computeTextWidth(element, mTextSize)
            if (width >= computeTextWidth) {
                computeTextWidth = width
            }
        }
        mTextHalf = computeTextWidth / 2f
    }

    private fun getWordCount(s: String): Float {
        var targetStr = s
        if (TextUtils.isEmpty(targetStr)) {
            return 0f
        }
        targetStr = targetStr.replace("[^\\x00-\\xff]".toRegex(), "**")
        val length = targetStr.length
        return length / 4.0f
    }

    private fun drawCircle(canvas: Canvas, drawStep: Int, text: String, dx: Int) {
        //绘制节点文字
        mPaint.color =
            if (this.mStep == drawStep) mUnderwayStringColor else if (this.mStep > drawStep) mStartedStringColor else mPreStringColor
        mPaint.textSize = textSizeActual()
        val textLineY = computeTextHeight(mTextSize) + paddingTop
        //(height - this.mRadius * 2 - 15).toFloat()
        canvas.drawText(
            text,
            dx - mTextHalf,
            textLineY,
            mPaint
        )

        val circleY = computeTextHeight(mTextSize) + mGap + paddingTop + mRadius
        //(height - this.mRadius - 1).toFloat()
        //绘制节点外边圆
        mPaint.color =
            if (this.mStep == drawStep) mUnderwayCircleColor else if (this.mStep > drawStep) mStartedCircleColor else mPreCircleColor
        mPaint.style = Paint.Style.STROKE
        mPaint.strokeWidth = 2.0f
        canvas.drawCircle(
            dx.toFloat(),
            circleY,
            mRadius,
            mPaint
        )

        //绘制节点内实心圆
        mPaint.style = Paint.Style.FILL
        canvas.drawCircle(
            dx.toFloat(),
            circleY,
            (this.mRadius - 5f),
            mPaint
        )
    }

    private fun drawLine(paramCanvas: Canvas, isStart: Boolean, startX: Int, endX: Int) {
        val circleY = computeTextHeight(mTextSize) + mGap + paddingTop + mRadius
        //(height - this.mRadius - 1).toFloat()
        mPaint.color = if (isStart) mStartedLineColor else mPreLineColor
        mPaint.style = Paint.Style.FILL

        if (isStart) {
            mPaint.strokeWidth = mLineWidth
            mPaint.pathEffect = null
        } else {
            mPaint.strokeWidth = 2f
            // 设置虚线效果
            mPaint.pathEffect = DashPathEffect(floatArrayOf(20f, 10f), 0f)
        }

        paramCanvas.drawLine(
            this.mRadius * 1.2f + startX,
            circleY,
            endX - this.mRadius * 1.2f,
            circleY,
            mPaint
        )
    }

    private fun convertSpToPx(context: Context, sp: Float): Float {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP,
            sp,
            context.resources.displayMetrics
        )
    }

    interface OnStepChangedListener {
        fun onChanged(view: StepLineView?, step: Int, stepStr: String?)
    }

    companion object {
        const val TAG: String = "StepLineView"
    }
}
